﻿using FFmpeg.AutoGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace VideoPlayer.FFmpegHandle
{
    public unsafe class FFmpegDll : IDisposable
    {
        AVFormatContext* fmt_ctx;
        //流队列中，视频流所在的位置
        int video_index = -1;
        int audio_index = -1;
        //解码上下文
        AVCodecContext* video_codec_ctx;
        AVCodecContext* audio_codec_ctx;

        //输出缓存大小
        int video_out_buffer_size;
        int audio_out_buffer_size;

        //输出缓存
        byte* video_out_buffer;
        byte* audio_out_buffer;

        //转码后输出的视频帧（如yuv转rgb24）
        AVFrame* video_out_frame = ffmpeg.av_frame_alloc();

        //格式转换上下文
        SwsContext* video_convert_ctx;
        SwrContext* audio_convert_ctx;

        //解码前数据包
        AVPacket* packet;

        //音频声道数量
        int nb_channels;

        AVSampleFormat out_sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16;//输出的采样格式 16bit PCM
        int sample_rate;//采样率
        ulong out_ch_layout = ffmpeg.AV_CH_LAYOUT_STEREO;//输出的声道布局：立体声

        byte_ptrArray4 _dstData;
        int_array4 _dstLinesize;
        //初始化FFmpeg 
        //@param *url 媒体地址（本地/网络地址）
        public int init_ffmpeg(string url)
        {
            ffmpeg.av_register_all();//注册组件
            ffmpeg.avformat_network_init();//支持网络流
            fmt_ctx = ffmpeg.avformat_alloc_context();


            fixed (AVFormatContext** fmt_ctx_ = &fmt_ctx)
            {
                //打开文件
                if (ffmpeg.avformat_open_input(fmt_ctx_, url, null, null) != 0)
                {
                    return -1;
                }
            }

            //查找流信息
            if (ffmpeg.avformat_find_stream_info(fmt_ctx, null) < 0)
            {
                return -1;
            }

            //找到流队列中，视频流所在位置
            for (int i = 0; i < fmt_ctx->nb_streams; i++)
            {
                if (fmt_ctx->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
                {
                    video_index = i;
                }
                if (fmt_ctx->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
                {
                    audio_index = i;
                }
            }

            //音视频流都没有找到
            if (video_index == -1 && audio_index == -1)
            {
                return -1;
            }

            //查找解码器
            video_codec_ctx = fmt_ctx->streams[video_index]->codec;
            audio_codec_ctx = fmt_ctx->streams[audio_index]->codec;
            AVCodec* video_codec = ffmpeg.avcodec_find_decoder(video_codec_ctx->codec_id);
            AVCodec* audio_codec = ffmpeg.avcodec_find_decoder(audio_codec_ctx->codec_id);

            //解码器没有找到
            if (video_codec == null && audio_codec == null)
            {
                return -1;
            }

            //打开解码器
            if (ffmpeg.avcodec_open2(video_codec_ctx, video_codec, null) < 0 & //两个都要打开，用&
                ffmpeg.avcodec_open2(audio_codec_ctx, audio_codec, null) < 0)
            {
                return -1;
            }

            //======视频转码准备======start======
            //计算输出缓存
            video_out_buffer_size = ffmpeg.av_image_get_buffer_size(
                AVPixelFormat.AV_PIX_FMT_RGB24,
                video_codec_ctx->width,
                video_codec_ctx->height,
                1);

            //输出缓存
            video_out_buffer = (byte*)ffmpeg.av_malloc((ulong)video_out_buffer_size);

            //准备一些参数，在视频格式转换后，参数将被设置值
            ffmpeg.av_image_fill_arrays(
                ref _dstData,//转换后的数据
              ref _dstLinesize,
                video_out_buffer, //视频buffer
                AVPixelFormat.AV_PIX_FMT_RGB24,//像素格式
                video_codec_ctx->width,
                video_codec_ctx->height,
                1);

            video_convert_ctx = ffmpeg.sws_getContext(//图片格式转换上下文
                video_codec_ctx->width,
                video_codec_ctx->height,
                video_codec_ctx->pix_fmt,
                video_codec_ctx->width,
                video_codec_ctx->height,
                AVPixelFormat.AV_PIX_FMT_RGB24,//转码为RGB像素
                ffmpeg.SWS_BICUBIC,
                null, null, null);
            //======视频转码准备======end======

            //======音频转码准备======start======
            AVSampleFormat in_sample_fmt = audio_codec_ctx->sample_fmt;//输入的采样格式
            sample_rate = audio_codec_ctx->sample_rate;//输入的采样率
            ulong in_ch_layout = audio_codec_ctx->channel_layout;//输入的声道布局

            audio_convert_ctx = ffmpeg.swr_alloc();
            ffmpeg.swr_alloc_set_opts(audio_convert_ctx, (long)out_ch_layout, out_sample_fmt, sample_rate, (long)in_ch_layout, in_sample_fmt, sample_rate, 0, null);
            ffmpeg.swr_init(audio_convert_ctx);

            nb_channels = ffmpeg.av_get_channel_layout_nb_channels(out_ch_layout);//获取声道个数
            audio_out_buffer = (byte*)ffmpeg.av_malloc((ulong)sample_rate * 2);//存储pcm数据
                                                                               //======音频转码准备======end======
            packet = ffmpeg.av_packet_alloc();
            //ffmpeg.av_init_packet(packet);
            return 0;
        }

        //读取一帧 -1：未取到 1：音频 2：视频 
        public int read_frame()
        {
            int ret = -1;

            //是否从packet中解出一帧，0为未解出
            int got_picture;
            int got_frame;

            //从packet中解出来的原始音视频帧
            AVFrame* original_audio_frame = ffmpeg.av_frame_alloc();
            AVFrame* original_video_frame = ffmpeg.av_frame_alloc();

            if (ffmpeg.av_read_frame(fmt_ctx, packet) == 0)
            {
                if (packet->stream_index == video_index)
                {
                    //解码。输入为packet，输出为original_video_frame
                    if (ffmpeg.avcodec_decode_video2(video_codec_ctx, original_video_frame, &got_picture, packet) >= 0)
                    {
                        if (got_picture >= 0)
                        {
                            //图片格式转换（上面图片转换准备的参数，在这里使用）
                            ffmpeg.sws_scale(video_convert_ctx,//图片转码上下文
                                original_video_frame->data.ToArray(),//原始数据
                        original_video_frame->linesize,//原始参数
                        0,//转码开始游标，一般为0
                        video_codec_ctx->height,//行数
                        video_out_frame->data,//转码后的数据
                        video_out_frame->linesize);
                            ret = 2;
                            //转码后，将buffer拷贝到非托管区内存，其他地方可以读取该内存；
                            //memcpy(video_buffer, video_out_buffer, video_out_buffer_size);
                           
                       }
                    }
                }
                else if (packet->stream_index == audio_index)
                {
                    if (ffmpeg.avcodec_decode_audio4(audio_codec_ctx, original_audio_frame, &got_frame, packet) >= 0)
                    {
                        if (got_frame > 0)
                        {
                            fixed (byte** audio_out_buffer_ = &audio_out_buffer)
                            {
                                //byte* b = original_audio_frame->data[1];

                                //音频格式转换
                                ffmpeg.swr_convert(audio_convert_ctx,//音频转换上下文
                                audio_out_buffer_,//输出缓存
                                sample_rate * 2,//每次输出大小
                                original_audio_frame->extended_data,//输入数据
                    original_audio_frame->nb_samples);//输入

                            }
                            audio_out_buffer_size = ffmpeg.av_samples_get_buffer_size(null, nb_channels, original_audio_frame->nb_samples, out_sample_fmt, 1);
                            ret = 1;
                        }
                    }
                }
            }

            ffmpeg.av_free_packet(packet);
            ffmpeg.av_free(original_audio_frame);
            ffmpeg.av_free(original_video_frame);
            return ret;
        }

        //获取音频缓存大小
        public int get_audio_buffer_size()
        {
            return audio_out_buffer_size;
        }

        //获取视频缓存大小
        public int get_video_buffer_size()
        {
            return video_out_buffer_size;
        }

        //获取音频帧
        public char* get_audio_frame()
        {
            return (char*)audio_out_buffer;
        }

        //获取视频帧
        public AVFrame get_video_frame()
        {
            return *video_out_frame;
            //return Marshal.AllocHGlobal(video_out_buffer_size);
            //return (char*)video_out_buffer;
        }

        public int_array8 get_linesize()
        {
            var linesize = new int_array8();
            linesize.UpdateFrom(_dstLinesize);
            return linesize;
        }

        //获取视频宽度
        public int get_video_width()
        {
            return video_codec_ctx->width;
        }

        //获取视频高度
        public int get_video_height()
        {
            return video_codec_ctx->height;
        }

        //释放资源
        public void release()
        {
            ffmpeg.sws_freeContext(video_convert_ctx);
            fixed (SwrContext** audio_convert_ctx_ = &audio_convert_ctx)
            {
                ffmpeg.swr_free(audio_convert_ctx_);

            }


            ffmpeg.av_free(video_out_frame);

            ffmpeg.av_free(video_out_buffer);
            ffmpeg.av_free(audio_out_buffer);

            ffmpeg.avcodec_close(video_codec_ctx);
            ffmpeg.avcodec_close(audio_codec_ctx);
            fixed (AVFormatContext** fmt_ctx_ = &fmt_ctx)
            {
                ffmpeg.avformat_close_input(fmt_ctx_);
            }
        }

        public void Dispose()
        {
            release();
        }


        //int main()
        //{

        //    int init_ret = init_ffmpeg(url);
        //    if (init_ret >= 0)
        //    {
        //        int read = -1;
        //        while ((read = read_frame()) != 1)//读取一帧，直到读取到数据
        //        {
        //            printf("未读取到数据\n");
        //        }
        //        //printf("高度:%d  宽度:%d  缓存大小:%d", get_video_height(), get_video_width(), get_video_buffer_size());
        //        //printf("\n=================================\n");
        //        //printf(get_video_frame());//打印出来，虽然打印出来的东西看不懂，但是证明已经获取到一帧的数据了

        //        while ((read = read_frame()) > 0)//循环读取音频
        //        {
        //            if (read == 1)
        //            {
        //                printf("缓存大小:%d", get_audio_buffer_size());
        //                printf("\n=================================\n");
        //                printf(get_audio_frame());
        //                printf("\n=================================\n");
        //            }
        //        }
        //    }
        //    else
        //    {
        //        printf("初始化失败!");
        //    }
        //    getchar();
        //    return 0;
        //}
    }
}
